Option Strict Off
Option Explicit On
Imports SlimDX
Friend Class CD3DPick
	
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	'
	'  Copyright (C) 2000 Microsoft Corporation.  All Rights Reserved.
	'
	'  File:       D3DPick.cls
	'  Content:    D3D Visual Basic Framework Pick object
	'              See raypack and viewport pick entrypoints
	'
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	
	
	Dim m_item() As D3D_PICK_RECORD
	Dim m_frame() As CD3DFrame
	Dim m_mesh() As CD3DMesh
	Dim m_count As Integer
	Dim m_maxsize As Integer
	Const kGrowSize As Short = 10
	
	'-----------------------------------------------------------------------------
	' Name: GetCount
	' Dest: returns number of items picked
	'-----------------------------------------------------------------------------
	Public Function GetCount() As Integer
		GetCount = m_count
	End Function
	
	'-----------------------------------------------------------------------------
	' Name: GetRecord
	' Desc: returns the properties of a given pick item
	'-----------------------------------------------------------------------------
	Public Sub GetRecord(ByRef i As Integer, ByRef a As Single, ByRef b As Single, ByRef dist As Single, ByRef triFaceid As Integer)
		a = m_item(i).a
		b = m_item(i).b
		dist = m_item(i).dist
		triFaceid = m_item(i).triFaceid
	End Sub
	
	'-----------------------------------------------------------------------------
	' Name: GetFrame
	' Desc: returns the frame of a given pick item
	'-----------------------------------------------------------------------------
	Public Function GetFrame(ByRef i As Integer) As CD3DFrame
		GetFrame = m_frame(i)
	End Function
	
	'-----------------------------------------------------------------------------
	' Name: GetMesh
	' Desc: returns the mesh of a given pick item
	'-----------------------------------------------------------------------------
	Public Function GetMesh(ByRef i As Integer) As CD3DMesh
		GetMesh = m_mesh(i)
	End Function
	
	'-----------------------------------------------------------------------------
	' Name: FindNearest
	' Desc: returns the index of the pick with the smallest distance (closest to viewer)
	'-----------------------------------------------------------------------------
	Public Function FindNearest() As Integer
		Dim q, i As Integer
		Dim mindist As Single
		q = -1
		mindist = 1E+38
		For i = 0 To m_count - 1
			If m_item(i).dist < mindist Then
				q = i
				mindist = m_item(i).dist
			End If
		Next 
		FindNearest = q
	End Function
	
	
	'-----------------------------------------------------------------------------
	' Name: FindFurthest
	' Desc: returns the index of the pick with the largest distance
	'-----------------------------------------------------------------------------
	Public Function FindFurthest() As Integer
		Dim q, i As Integer
		Dim maxdist As Single
		q = -1
		maxdist = -1E+38
		For i = 0 To m_count - 1
			If m_item(i).dist < maxdist Then
				q = i
				maxdist = m_item(i).dist
			End If
		Next 
		FindFurthest = q
	End Function
	
	
	'-----------------------------------------------------------------------------
	' Name: Destroy
	' Desc: Release all references
	'-----------------------------------------------------------------------------
    Public Sub Destroy()
        ReDim m_mesh(0)
        ReDim m_frame(0)
        m_count = 0
        m_maxsize = 0
    End Sub
	
	
	'-----------------------------------------------------------------------------
	' Name: ViewportPick
	' Params:
	'    frame      parent of frame heirarchy to pick from
	'    x          x screen coordinate in pixels
	'    y          y screen coordinate in pixels
	'
	' Note: After call GetCount to see if any objets where hit
	'-----------------------------------------------------------------------------
    Public Function ViewportPick(frame As CD3DFrame, x As Single, y As Single) As Boolean
        Destroy()

        Dim viewport As Direct3D9.Viewport
        Dim world As New Matrix
        Dim proj As New Matrix
        Dim view As New Matrix

        'NOTE the following functions will fail on PURE HAL devices
        'use ViewportPickEx if working with pureHal devices

        viewport = g_dev.Viewport
        world = g_identityMatrix
        view = g_dev.GetTransform(Direct3D9.TransformState.View)
        proj = g_dev.GetTransform(Direct3D9.TransformState.Projection)

        Return ViewportPickEx(frame, viewport, proj, view, world, x, y)

    End Function
	
    Public Function GetXYZ(frame As CD3DFrame, x As Single, y As Single, zCam As Single) As Vector3
        Destroy()

        Dim viewport As Direct3D9.Viewport
        Dim world As New Matrix
        Dim proj As New Matrix
        Dim view As New Matrix
        Dim vNear, vIn As Vector3
        Dim currentMatrix As New Matrix
        Dim NewWorldMatrix As New Matrix
        Dim mn, mx As Vector3
        Dim mp As Vector3
        Dim v As Single

        With mn
            .X = 32000
            .Y = 32000
            .Z = 32000
        End With
        With mx
            .X = -32000
            .Y = -32000
            .Z = -32000
        End With

        frame.GetLocalBox(mn, mx)

        mp.X = mx.X - mn.X
        mp.Y = mx.Y - mn.Y
        mp.Z = mx.Z - mn.Z

        v = Max(Max((mp.X), (mp.Y)), (mp.Z))

        viewport = g_dev.Viewport
        world = g_identityMatrix
        view = g_dev.GetTransform(Direct3D9.TransformState.View)
        proj = g_dev.GetTransform(Direct3D9.TransformState.Projection)

        currentMatrix = frame.GetMatrix

        'Setup our basis matrix for this frame
        NewWorldMatrix = Matrix.Multiply(currentMatrix, world)

        vIn.X = x : vIn.Y = y

        ' Compute point on Near Clip plane at cursor
        vIn.Z = 1 - (1 / -zCam)
        
        vNear = Vector3.Unproject(vIn, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinZ, viewport.MaxZ, NewWorldMatrix)
        
        Return vNear
    End Function
	
    Public Function GetDirection(frame As CD3DFrame, Camera As Vector3) As Vector3
        Destroy()

        Dim viewport As Direct3D9.Viewport
        Dim world As New Matrix
        Dim proj As New Matrix
        Dim view As New Matrix
        Dim vDir, vNear, vIn, vFar, vPos As Vector3
        Dim bHit As Boolean
        Dim i As Integer
        Dim currentMatrix As New Matrix
        Dim NewWorldMatrix As New Matrix
        Dim mn, mx As Vector3
        Dim mp As Vector3
        Dim v As Single

        With mn
            .X = 32000
            .Y = 32000
            .Z = 32000
        End With
        With mx
            .X = -32000
            .Y = -32000
            .Z = -32000
        End With

        frame.GetLocalBox(mn, mx)

        mp.X = mx.X - mn.X
        mp.Y = mx.Y - mn.Y
        mp.Z = mx.Z - mn.Z

        v = Max(Max((mp.X), (mp.Y)), (mp.Z))

        viewport = g_dev.Viewport
        world = g_identityMatrix
        view = g_dev.GetTransform(Direct3D9.TransformState.View)
        proj = g_dev.GetTransform(Direct3D9.TransformState.Projection)


        currentMatrix = frame.GetMatrix

        'Setup our basis matrix for this frame
        NewWorldMatrix = Matrix.Multiply(currentMatrix, world)


        vIn.X = Camera.X : vIn.Y = Camera.Y

        ' Compute point on Near Clip plane at cursor
        vIn.Z = 0
        ' DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Unproject(vNear, vIn, viewport, proj, view, NewWorldMatrix)
        vNear = Vector3.Unproject(vIn, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinZ, viewport.MaxZ, NewWorldMatrix)
        'compute point on far clip plane at cursor
        vIn.Z = Camera.Z
        '  DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Unproject(vFar, vIn, viewport, proj, view, NewWorldMatrix)
        vFar = Vector3.Unproject(vIn, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinZ, viewport.MaxZ, NewWorldMatrix)
        'Comput direction vector
        ' DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Subtract(vDir, vFar, vNear)
        vDir = Vector3.Subtract(vFar, vNear)

        Return vDir
    End Function
	
	
	
	
	
	
	'-----------------------------------------------------------------------------
	' Name: ViewportPickEx
	' Desc: Aux function for ViewportPick
	'-----------------------------------------------------------------------------
    Public Function ViewportPickEx(frame As CD3DFrame, viewport As Direct3D9.Viewport, proj As Matrix, view As Matrix, world As Matrix, x As Single, y As Single) As Boolean

        If frame Is Nothing Then Return False
        If frame.Enabled = False Then Return False
        On Error Resume Next

        Dim vFar, vIn, vNear, vDir As Vector3
        Dim bHit As Boolean
        Dim i As Integer




        Dim currentMatrix As New Matrix
        Dim NewWorldMatrix As New Matrix

        currentMatrix = frame.GetMatrix

        'Setup our basis matrix for this frame
        ' DxVBLibA.D3DXMATH_MATRIX.D3DXMatrixMultiply(NewWorldMatrix, currentMatrix, world)
        NewWorldMatrix = Matrix.Multiply(currentMatrix, world)
        vIn.X = x : vIn.Y = y

        'Compute point on Near Clip plane at cursor
        vIn.Z = 0
        'DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Unproject(vNear, vIn, viewport, proj, view, NewWorldMatrix)
        vNear = Vector3.Unproject(vIn, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinZ, viewport.MaxZ, NewWorldMatrix)


        'compute point on far clip plane at cursor
        vIn.Z = 1
        '  DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Unproject(vFar, vIn, viewport, proj, view, NewWorldMatrix)
        vFar = Vector3.Unproject(vIn, viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinZ, viewport.MaxZ, NewWorldMatrix)
        'Comput direction vector
        ' DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Subtract(vDir, vFar, vNear)
        vDir = Vector3.Subtract(vFar, vNear)




        Dim item As D3D_PICK_RECORD
        item.Init()


        'Check all child meshes
        'Even if we got a hit we continue as the next mesh may be closer
        Dim childMesh As CD3DMesh
        For i = 0 To frame.GetChildMeshCount() - 1

            childMesh = frame.GetChildMesh(i)

            If Not childMesh Is Nothing Then

                Dim vRay As New Ray(vNear, vDir)

                childMesh.mMesh.Intersects(vRay, item.dist, item.triFaceid, item.hit)

                '  g_d3dx.Intersect(childMesh.Mesh, vNear, vDir, item.hit, item.triFaceid, item.a, item.b, item.dist)
            End If

            If item.hit(0).Distance <> 0 Then
                InternalAddItem(frame, childMesh, item)
                item.hit(0).Distance = 0
            End If
            bHit = True
        Next

        'check pick for all child frame
        Dim childFrame As CD3DFrame
        For i = 0 To frame.GetChildFrameCount() - 1
            childFrame = frame.GetChildFrame(i)
            bHit = bHit Or ViewportPickEx(childFrame, viewport, proj, view, NewWorldMatrix, x, y)
        Next

        Return bHit

    End Function
	
	
	
	'-----------------------------------------------------------------------------
	' Name: RayPick
	' Desc: given a ray cast it into a scene graph
	' Params:
	'    frame      parent of frame heirarchy to pick from
	'    vOrig      origen of the ray to cast
	'    vDir       direction of the ray
	'
	' Note: the following functions will fail on PURE HAL devices
	'       use RayPickEx if working with pureHal devices
	'       Call getCount to see if the ray hit any objects
	'
	'-----------------------------------------------------------------------------
    Public Function RayPick(frame As CD3DFrame, vOrig As Vector3, vDir As Vector3) As Object
        Destroy()

        Dim world As New Matrix


        world = g_dev.GetTransform(Direct3D9.TransformState.World)

        Return RayPickEx(frame, world, vOrig, vDir)

    End Function
	
	'-----------------------------------------------------------------------------
	' Name: RayPickEx
	' Desc: Aux function for RayPickEx
	'-----------------------------------------------------------------------------
    Public Function RayPickEx(frame As CD3DFrame, worldmatrix As Matrix, vOrig As Vector3, vDir As Vector3) As Boolean

        Dim NewWorldMatrix As Matrix 'world matrix for this stack frame
        Dim InvWorldMatrix As Matrix 'world matrix for this stack frame
        Dim currentMatrix As Matrix
        Dim i As Integer
        Dim bHit As Boolean
        Dim vNewDir, vNewOrig As Vector3

        If frame Is Nothing Then Exit Function

        currentMatrix = frame.GetMatrix

        'Setup our basis matrix for this frame
        'DxVBLibA.D3DXMATH_MATRIX.D3DXMatrixMultiply(NewWorldMatrix, currentMatrix, worldmatrix)
        'DxVBLibA.D3DXMATH_MATRIX.D3DXMatrixInverse(InvWorldMatrix, det, NewWorldMatrix)
        NewWorldMatrix = Matrix.Multiply(currentMatrix, worldmatrix)
        InvWorldMatrix = Matrix.Invert(NewWorldMatrix)
        InvWorldMatrix = Matrix.Transpose(InvWorldMatrix)

        ' we want to compute vdir and vOrig in model space
        ' note we use TransformNormal so we dont translate vDir
        ' just rotate it into a new dir
        'Call DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3TransformNormal(vNewDir, vDir, InvWorldMatrix)
        'Call DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3TransformCoord(vNewOrig, vOrig, InvWorldMatrix)

        vNewDir = Vector3.TransformNormal(vDir, InvWorldMatrix)
        vNewOrig = Vector3.TransformCoordinate(vOrig, InvWorldMatrix)


        Dim item As D3D_PICK_RECORD
        item.Init()


        'Check all child meshes
        'Even if we got a hit we continue as the next mesh may be closer
        Dim childMesh As CD3DMesh
        For i = 0 To frame.GetChildMeshCount() - 1

            childMesh = frame.GetChildMesh(i)

            If Not childMesh Is Nothing Then
                '				Call DxVBLibA.D3DXMATH_VECTOR3.D3DXVec3Scale(vDir, vDir, 1000) 'Workaround for d3dx Intersect bug
                vDir = Vector3.Multiply(vDir, 1000) 'Workaround for d3dx Intersect bug

                Dim vRay As New Ray(vOrig, vDir)

                childMesh.mMesh.Intersects(vRay, item.dist, item.triFaceid, item.hit)

                ' g_d3dx.Intersect(childMesh.mMesh, vNewOrig, vDir, item.hit, item.triFaceid, item.a, item.b, item.dist)
            End If

            If item.hit(0).Distance <> 0 Then
                InternalAddItem(frame, childMesh, item)
                item.hit(0).Distance = 0
            End If
            bHit = True
        Next

        'check pick for all child frame
        Dim childFrame As CD3DFrame
        For i = 0 To frame.GetChildFrameCount() - 1
            childFrame = frame.GetChildFrame(i)
            bHit = bHit Or RayPickEx(childFrame, NewWorldMatrix, vOrig, vDir)
        Next

        RayPickEx = bHit

    End Function
	
	
	
	
	'-----------------------------------------------------------------------------
	' InternalAddItem
	'-----------------------------------------------------------------------------
    Private Sub InternalAddItem(parentFrame As CD3DFrame, Mesh As CD3DMesh, item As D3D_PICK_RECORD)

        If m_maxsize = 0 Then
            ReDim m_item(kGrowSize)
            ReDim m_mesh(kGrowSize)
            ReDim m_frame(kGrowSize)
            m_maxsize = kGrowSize
        ElseIf m_count >= m_maxsize Then
            ReDim Preserve m_item(m_maxsize + kGrowSize)
            ReDim Preserve m_frame(m_maxsize + kGrowSize)
            ReDim Preserve m_mesh(m_maxsize + kGrowSize)
            m_maxsize = m_maxsize + kGrowSize
        End If

        m_mesh(m_count) = Mesh
        m_frame(m_count) = parentFrame
        m_item(m_count) = item

        m_count = m_count + 1
    End Sub
End Class